#pragma once

/* wfspan层 */
// #define WFSPAN_FAST_BUT_MAYBUGGY	/* 可能修改用户数据 */
#include <metalc/block/wfspan_obj.h>
#include <metalc/block/cobj.h>
/* 大型 lobj维护 */
OU2INFUN(cobj, wfspan_lobj)

SAI void _wfspan_pinit(const mlfunc_t * cont){
	PARAM_USED(cont);
	INFO("_wfspan pinit\n");
}

SAI void _wfspan_tinit(const mlfunc_t * cont, perm_allocf_t *alloc, wfspan_conf_t *conf){
	INFO("_wfspan tinit\n");
	mtls_t *tls = mtls_get();
	ASSERT(tls);
	// ASSERT(tls->objs[cont->lidx] == NULL_PTR);
	size_t sz = wfspanobj_size(conf->l1_clsnr, conf->l2_clsnr);
	void *mem = alloc(sz);
	ASSERT(mem);

	/* TODO: 解决重复初始化的问题 */
	if(!tls->objs[cont->lidx]) {
		tls->objs[cont->lidx] = wfspan_init(mem, sz, conf);
	}
	else{
		INFO("_wfspan tinit already %p\n", tls->objs[cont->lidx]);
	}
}

SAI void *wfslab_oomhandler(mreq_t *req, size_t size, const mlfunc_t * cont, wfspan_conf_t *conf){
	void *ret;

	/* 一次申请更多? 或者加强cache? */

	req->size = size;
	req->align = req->align < conf->mslab_size ? conf->mslab_size : req->align;
	ret = cont->lalloc(req);
	INFO("[wfspan] oom alloc from upper %p, size %lx\n", ret, req->size);

	return ret;
}

SAI void *_wfspan_alloc(mreq_t *req, const mlfunc_t * cont, cls2maxnr * const u2mnr, wfspan_conf_t *conf, \
INFUN(wfspan_clspol, clspol), INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	size_t req_size = req->size;
	/* 自对齐方式 */
	if (UNLIKELY(req->align != 0))
		req_size = align_up(req_size, req->align);
	if (__MLAYER_INVALID_REQSZ(conf->max_sz, req_size)) return cont->lalloc(req);

	void *ret;
	void *upper_mem = NULL_PTR;
	mtls_t *tls = mtls_by_req(req);
	wfspan_obj_t *l = (wfspan_obj_t *)MLAYER_GET_OBJ(tls, cont);
	ASSERT(l);

	if (l2clspol){
		if (UNLIKELY(req_size > conf->l1_maxsz)) {
			/* 按l2最小大小对齐 */
			req_size = align_up(req_size + lobj->mocupysz(), conf->l2_minsz) ;
			szcls_t cls = l2clspol->sz2cls(req_size) + conf->l1_clsnr;
			size_t aligned_size = l2clspol->cls2sz(cls - conf->l1_clsnr);
			/* 尝试窃取 */
		#ifndef WFSPAN_FAST_BUT_MAYBUGGY	/* 可能修改用户数据 */
			mslab_t *cur_slab = wfspan_lockfree_fastpath(l, cls, 0, conf);	/* 不能窃取，会有修改用户数据的可能性 */
			if (!cur_slab) cur_slab = wfspan_waitfree_acquire(l, cls, 1, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);	/* 从自己开始满足 */
		#else
			mslab_t *cur_slab = wfspan_lockfree_fastpath(l, cls, 1, conf);	
			if (!cur_slab) cur_slab = wfspan_waitfree_acquire(l, cls, 0, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);	
		#endif
			if (!cur_slab) {
				/* 向上申请 */
				cur_slab = (mslab_t *)wfslab_oomhandler(req, aligned_size, cont, conf);
				/* TODO: 设置cobj */
				if (!cur_slab) return NULL_PTR;
				lobj->set_cobj(cur_slab, aligned_size, 1);
			}
			
			ret = lobj->ptr2uptr(cur_slab);
			req->ptr = cur_slab;
			req->size = aligned_size;
			req->nr = 1;
			INFO("[wfspan l2] alloc %p, size %lx, cls %u\n", ret, aligned_size, cls);
			return ret;
		}
	}

	mslab_t *cur_slab = NULL_PTR;
	szcls_t cls = clspol->sz2cls(req_size);
	hword_t unit_size = (hword_t)clspol->cls2sz(cls);
	hword_t max_nr = u2mnr(cls);
	INFO("[wfspan] try alloc, need size %lx, utsz %x, cls %u\n", req_size, unit_size, cls);
	wfspan_cls_t *szclsp = wfspanobj_get_clsarr(l);
	
	/* 先尝试本地申请 */
	ret = wfspan_local_alloc(l, szclsp, cls, unit_size, conf);
	if (LIKELY(ret)) goto FIN; 


	/* 优先从本地full_list中满足获取可以降低WCET */
	ret = wfspan_local_full_alloc(l, szclsp, cls, unit_size, max_nr, conf);
	if (LIKELY(ret)) goto FIN;

	/* 本地申请失败，尝试快速路径窃取 */
	ret = wfspan_acquire_from_others(l, wfspan_lockfree_fastpath(l, cls, 1, conf), szclsp, cls, unit_size, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);
	if (LIKELY(ret)) goto FIN;
	ret = wfspan_acquire_from_others(l, NULL_PTR, szclsp, cls, unit_size, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);
	if (LIKELY(ret)) goto FIN;

	ASSERT(!ret);
	/* 窃取失败，继续向上申请 */
	if (l2clspol){
		/* 向l2窃取 */
		szcls_t spcls = conf->l1_clsnr;
	#ifndef WFSPAN_FAST_BUT_MAYBUGGY	/* 可能修改用户数据 */
		upper_mem = wfspan_lockfree_fastpath(l, spcls, 0, conf);	/* 不能窃取，会有修改用户数据的可能性 */
		if (!upper_mem) upper_mem = wfspan_waitfree_acquire(l, spcls, 1, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);
	#else
		upper_mem = wfspan_lockfree_fastpath(l, spcls, 1, conf);	/* 不能窃取，会有修改用户数据的可能性 */
		if (!upper_mem) upper_mem = wfspan_waitfree_acquire(l, spcls, 0, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);
	#endif
	}
	if (!upper_mem) upper_mem = wfslab_oomhandler(req, conf->mslab_size, cont, conf);
	
	if (LIKELY(upper_mem)){
		/* 申请成功 */
		ret = wfslab_local_init_alloc(l, upper_mem, conf->mslab_size, szclsp, cls, unit_size, max_nr);
		
	}
FIN:	
	INFO("[wfspan] " PRIN_RED() "alloc " PRIN_DFT() " %p, need size %lx, real size %x, cls %u\n", ret, req_size, unit_size, cls);
	req->size = (size_t)unit_size;
	req->ptr = (void *)ret;
	req->nr = 1;
	return ret;
}
SAI size_t wfslab_local_free(wfspan_obj_t *l, mslab_t *s, void *free_ptr, szcls_t cls, sword_t slab_max_nr, dlist_t **phead, dlist_t **ptail, wfspan_conf_t *conf){
	PARAM_USED(cls);
	/* 本地释放路径 */
	sword_t consumed_cnt = mslab_local_free(s, (mlistnode_t *)free_ptr, (mlistnode_t *)free_ptr, 1);
	INFO("[wfspan] local free %p in [%p], cur local cnt %ld, max nr %ld\n", free_ptr, s, consumed_cnt, mslab_visible_nr(s));
			
	/* 本地释放应该保留? */
	/* 需要在本地释放路径判断全局大小吗? */
	/* 负数一定小于 0 */
	if ((consumed_cnt >= 0) &&
		/*(mslab_visible_nr(s) + (sword_t)consumed_cnt == slab_max_nr)*/
		__mslab_is_full(s, slab_max_nr)){
		/* 释放节点 */
		wfspan_remove_local_list(l, s);


		/* 优先放到本地full_list的头部 */
		wfspan_push_full_local_list(l, cls, s);
		// ASSERT(l->full_nr >= 1 && l->full_nr <= conf->slab_limits && l->full_nr <= l->slab_nr);

		/* 检查是否出队 */
		/* 是否需要出队? 必须需要吧 */
		if (l->slab_nr >= conf->slab_limits) {
			/* 满的出队 */
			// return wfspan_clear_full_local_list(l, phead, ptail);
			
			mslab_t *retnode = wfspan_pop_tail_full_local_list(l);
			*phead = &retnode->node; *ptail = &retnode->node;
			return 1;
		}

		// /* 直接出队试试?这样full alloc步骤也省了 */
		// mslab_t *retnode = s;
		// *phead = &retnode->node; *ptail = &retnode->node;
		// return 1;
		
	}
	// ASSERT(l->slab_nr <= conf->slab_limits);

	return 0;
}
SAI size_t wfslab_remote_free(wfspan_obj_t *l, mslab_t *s, void *free_ptr, szcls_t cls, sword_t slab_max_nr, dlist_t **phead, dlist_t **ptail, wfspan_conf_t *conf){
	PARAM_USED(slab_max_nr);
	size_t ret_nr = 0;
	/* 使用dlist出队一定要确保node是第一个成员 */
	ASSERT(OFFSETOF(mslab_t, node) == 0);
	wfspan_cls_t *szclsp = wfspanobj_get_clsarr(l);

	INFO("[wfspan] remote free %p in [%p]\n", free_ptr, s);
	/* 远程释放 */
	sword_t cnt = mslab_remote_free(s, (mlistnode_t *)free_ptr, (mlistnode_t *)free_ptr, 1);
	
	if (LIKELY(cnt != 0))
		return 0;
	/* 等于0则负责回收 */

	INFO("[wfspan] reclaim [%p]\n", s);
	mlistnode_t *reclaimed_list = mlist_hd_exchg(&s->rlist, NULL_PTR);
	ASSERT(s->consumed_cnt == 0);
	ASSERT(reclaimed_list != NULL_PTR);
	atm_st(&s->allocator, l);
	s->flist = reclaimed_list;
	ASSERT(s->cls == cls);
	/* 插入到尾部等待复用 */
	wfspan_push_local_list_tail(l, szclsp, s, cls);
	/* 插入头部减少碎片 */
	// wfspan_push_local_list_head(l, szclsp, s, cls);
	// TODO: 最近最少使用的出队
	if (l->slab_nr >= conf->slab_limits)
	{
		/* 大于本地限制则出队 */

		/* 优先出队本地已满的 */
		if (l->full_nr != 0) {
			/* 出队已满的最后一个 */
			// ret_nr = wfspan_clear_full_local_list(l, phead, ptail);
			mslab_t *retnode = wfspan_pop_tail_full_local_list(l);
			*phead = &retnode->node; *ptail = &retnode->node;
			ret_nr = 1;
		}
		else {
			/* 出队 */
			ASSERT(!list_is_empty(&l->non_full_lru_list));
			/* 出队 */
			mslab_t *rep = wfspan_pop_recently_unused(l);
			// rep->allocator = NULL_PTR;
			szcls_t repcls = rep->cls;
			INFO("[wfspan] insert recently unused [%p] to spmc list\n", rep);
			// wfspan_researved_mslabn(l, repcls, rep, rep);
			/* 使用该节点清理队列 */
			/* TODO: 增加一个限制n，以限制回收步骤 */
			// ret = wfspan_clean_spmclists(l, repcls, (spmcnode_t *)&rep->node, free_slab_nr, conf);
			/* 只瞥4个 */
			ret_nr = wfspan_clean_spmclists_limit(l, repcls, rep, phead, ptail, 4, conf);
			
		}
	}

	INFO("[wfspan] reclaim [%p] to local list\n", s);
	return ret_nr;
}
/* TODO: 查找Allocator的时候也可以通过本地索引判断？这样也能减少Cache行的竞争 */


static inline bool_t wfslab_oofhandler(mreq_t *req, dlist_t *head, dlist_t *tail, size_t nr, const mlfunc_t * cont, wfspan_conf_t *conf)
{
	INFO("[wfspan] oof free slab %p - %p, nr %lx, ptr %p, size %lx\n", head, tail, nr, req->ptr, conf->mslab_size);
	req->ptr = head;
	req->sptr = tail;
	req->size = conf->mslab_size;
	req->nr = nr;
	return cont->lfree(req);
}
/* 优化方案1， max_nr和owner_tid编码 */
SAI bool_t _wfspan_free(mreq_t *req, const mlfunc_t * cont, cls2maxnr * const u2mnr, wfspan_conf_t *conf, \
INFUN(wfspan_clspol, clspol), INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	size_t get_size = req->size;
	if (__MLAYER_INVALID_REQSZ(conf->max_sz, get_size)) return cont->lfree(req);

	mtls_t *tls = mtls_by_req(req);
	wfspan_obj_t *l = (wfspan_obj_t *)MLAYER_GET_OBJ(tls, cont);
	ASSERT(l);

	/* 启用l2 */
	if (l2clspol){
		if (UNLIKELY(get_size >= conf->l2_minsz)) {
			/* 直接插入到spmc链表中 */
			szcls_t cls = l2clspol->sz2cls(get_size) + conf->l1_clsnr;
			mslab_t *cur_slab = (mslab_t *)lobj->uptr2ptr(req->ptr); /* TODO:需要转换 */;
			INFO("[wfspan l2] free %p, size %lx, cls %u\n", cur_slab, get_size, cls);
			/* 不做检查 */
			__wfspan_researved_mslabn(l, cls, cur_slab, cur_slab);
			return TRUE;
		}
	}
	/* wfslab释放 */
	mslab_t *s = mslab_byptr(req->ptr, conf->mslab_size);
	wfspan_obj_t *r = (wfspan_obj_t *)s->allocator;
	// 
	PARAM_USED(clspol);
	szcls_t cls = req->cls;
	hword_t max_nr = u2mnr(cls);
	INFO("[wfspan] try free %p, size %lx, cls %u\n", req->ptr, req->size, cls);
	/* cid_t tid = tls->apdstls.tinfo.tid; */
	ASSERT(req->nr == 1);
	
	ASSERT(s->cls == cls);

	/* 释放 */
	size_t ret_nr;
	dlist_t *ret_head;
	dlist_t *ret_tail;
	if (r == l){
		/* 本地释放 */
		ret_nr = wfslab_local_free(l, s, req->ptr, cls, (sword_t)max_nr, &ret_head, &ret_tail, conf);
	}else{
		ret_nr = wfslab_remote_free(l, s, req->ptr, cls, (sword_t)max_nr, &ret_head, &ret_tail, conf);
	}
	// ASSERT(l->slab_nr <= conf->slab_limits);
	/* 释放后需要向上层释放 */
	if (UNLIKELY(ret_nr)) {
		ASSERT(ret_head && ret_tail);
		if (l2clspol){
			/* 先向自己释放 */
			szcls_t cls = conf->l1_clsnr;
			INFO("[wfspan l2] free %p - %p, size %lx, cls %u\n", ret_head, ret_tail, get_size, cls);
			/* 不做检查 */
			// if
			__wfspan_researved_mslabn(l, cls, (mslab_t *)ret_head, (mslab_t *)ret_tail);
			return TRUE;
		}
		
		return wfslab_oofhandler(req, ret_head, ret_tail, ret_nr, cont, conf);
	}
	
	return TRUE;
}

SAI size_t _wfspan_sizeof(mreq_t *req, const mlfunc_t * cont, wfspan_conf_t *conf, INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	ASSERT(req->ptr);

	if (!is_ptr_aligned(req->ptr, conf->mslab_size)){
		/* 节省free cls计算的时间 */
		mslab_t *s = mslab_byptr(req->ptr, conf->mslab_size);
		word_t cls_size = s->cls_size;
		szcls_t cls = word_high(cls_size);
		hword_t unit_size = word_low(cls_size);
		INFO("[wfspan] sizeof %p, sz %x, cls %u\n", req->ptr, unit_size, cls);
		
		req->size = unit_size;
		req->cls = cls;
		/* WFSPAN_PREFETCH(req->ptr); */ /* 提前预取，因为插入队列一定需要访问 */
		return req->size;
	} else if (l2clspol) {
		/* 大的 */
		
		void *ptr = lobj->uptr2ptr(req->ptr);
		size_t size = lobj->ptr_getsize(ptr);
		req->ptr = ptr;
		req->size = size;
		INFO("[wfspan l2] sizeof %p, sz %lx\n", req->ptr, size);
		return size - lobj->mocupysz();
	}

	return cont->lsizeof(req);
}

/* 定义 
 * min_bit - 管理的最小bit位
 * max_bit - 管理的最大bit位
 * slab_size - slab大小
 * slab_limit - 线程持有的slab个数限制，推荐为41
 * p_frac - travel的分数，推荐为0，越大内存占用越高，性能越好
 * help_quota - 单次帮助数量，推荐为1
 * cls_comp - szcls策略
 * u2mfunc - unit_size到max_nr的映射函数
 * perm_alloc - 永久对象分配函数
*/
#define DEF_WFSLAB_MLAYER(name, min_bit, max_bit, slab_size, slab_limit, p_frac, help_quota, cls_comp, u2mfunc, perm_alloc)\
DEF_MLAYER_BASIC(name, BIT(max_bit), 1)\
static const wfspan_conf_t name##_conf = { .mslab_size = slab_size, .slab_limits = slab_limit, .travel_frac = p_frac, \
.max_help_quota = help_quota, .max_sz = _maxsz_##cls_comp, .l1_minsz = BIT(min_bit), .l1_maxsz = _maxsz_##cls_comp, \
.l2_minsz = 0, .l2_maxsz = 0, .l1_clsnr = _clsnr_##cls_comp, .l2_clsnr = 0 }; \
SAI void name##_pinit(const mlfunc_t * cont){ return _wfspan_pinit(cont); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _wfspan_tinit(cont, perm_alloc, &name##_conf); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_alloc(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), NULL_PTR, NULL_PTR);\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_free(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), NULL_PTR, NULL_PTR);\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_sizeof(req, cont, &name##_conf, NULL_PTR, NULL_PTR); }

